using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
using AgilePlannerDT.Data;

namespace AgilePlannerDT.Utils
{
    /// <summary>
    /// Collection of helper methods for the application.
    /// </summary>
    public class Util
    {
        /// <summary>
        /// Converts between relative and absolute coordinates. Where relative means relative to
        /// an index card and absolute means coordinates inside the application main window.
        /// </summary>
        /// <param name="relPoint">The rel point.</param>
        /// <param name="center">The center.</param>
        /// <param name="angle">The angle.</param>
        /// <param name="transX">The trans X.</param>
        /// <param name="transY">The trans Y.</param>
        /// <returns></returns>
        public static Point RelativeToAbsoluteCoordinates(Point relPoint, Point center, double angle, double transX, double transY)
        {
            double x, y;

            x = relPoint.X - center.X;
            y = relPoint.Y - center.Y;

            double x2 = x * Math.Cos(Util.ToRadians(angle)) - y * Math.Sin(Util.ToRadians(angle));
            double y2 = y * Math.Cos(Util.ToRadians(angle)) + x * Math.Sin(Util.ToRadians(angle));

            x = x2 + center.X;
            y = y2 + center.Y;

            return new Point(transX + x*Properties.Settings.Default.Scale, transY + y*Properties.Settings.Default.Scale);
        }

        /// <summary>
        /// Determines whether a given point is inside of a (convex) polygon.
        /// </summary>
        /// <param name="p">The point.</param>
        /// <param name="polygon">The polygon.</param>
        /// <returns>
        /// 	<c>true</c> if [is point inside polygon] [the specified p]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsPointInsidePolygon(Point p, Point[] polygon)
        {
            /*
             * http://rw7.de/ralf/inffaq/polygon.html#konvex
             * Algorithm copied and adjusted
             */

            double[] m, n, o;

            m = new double[polygon.Length];
            n = new double[polygon.Length];
            o = new double[polygon.Length];

            double bx, by;
            double cx = polygon[polygon.Length - 1].X;
            double cy = polygon[polygon.Length - 1].Y;

            for (int i = 0; i < polygon.Length; i++)
            {
                bx = polygon[i].X;
                by = polygon[i].Y;

                m[i] = bx * cy - by * cx;
                n[i] = by - cy;
                o[i] = cx - bx;

                cx = bx;
                cy = by;
            }

            if (m.Length <= 0)
                return false;
            double A = 0, B;
            for (int i = 0; i < m.Length; i++)
            {
                B = m[i] + p.X * n[i] + p.Y * o[i];
                if (((B < 0) && (A > 0)) || ((B > 0) && (A < 0)) || (B == 0))
                    return false;
                A = B;
            }
            return true;
        }

        /// <summary>
        /// Gets the signed angle between two vectors in radians.
        /// </summary>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        public static double GetSignedAngleBetweenVectors2D(Point v1, Point v2)
        {
            /*
             * http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
             */

            Point v11 = Normalize2DVector(v1);
            Point v21 = Normalize2DVector(v2);

            return Math.Atan2(v21.Y, v21.X) - Math.Atan2(v11.Y, v11.X);
        }

        /// <summary>
        /// Gets the euclidian distance between two points.
        /// </summary>
        /// <param name="p1">The p1.</param>
        /// <param name="p2">The p2.</param>
        /// <returns></returns>
        public static double GetDistance2D(Point p1, Point p2)
        {
            return Math.Sqrt(
                Math.Pow((p1.X - p2.X), 2) +
                Math.Pow((p1.Y - p2.Y), 2));
        }

        /// <summary>
        /// Normalizes the given vector.
        /// </summary>
        /// <param name="v">The v.</param>
        /// <returns></returns>
        public static Point Normalize2DVector(Point v)
        {
            double length = GetVector2DLength(v);

            return new Point(v.X / length, v.Y / length);
        }

        /// <summary>
        /// Gets the length of the vector.
        /// </summary>
        /// <param name="v">The v.</param>
        /// <returns></returns>
        public static double GetVector2DLength(Point v)
        {
            return GetDistance2D(v, new Point(0, 0));
        }

        /// <summary>
        /// Converts an angle in degrees to an angle in radians
        /// </summary>
        /// <param name="degrees">The degrees.</param>
        /// <returns></returns>
        public static double ToRadians(double degrees)
        {
            return degrees * Math.PI / 180;
        }

        /// <summary>
        /// Converts an angle in radians to an angle in degrees
        /// </summary>
        /// <param name="radians">The radians.</param>
        /// <returns></returns>
        public static double ToDegrees(double radians)
        {
            return radians * 180 / Math.PI;
        }
    }
}
